<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>IPC之一：使用匿名管道进行父子进程间通信的例子 - whowin - 发表我个人原创作品的技术博客</title>
  <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>

<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />

<meta name="theme-color" content="#f8f5ec" />
<meta name="msapplication-navbutton-color" content="#f8f5ec">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#f8f5ec">


<meta name="author" content="whowin" /><meta name="description" content="IPC 是 Linux 编程中一个重要的概念，IPC 有多种方式，本文主要介绍匿名管道(又称管道、半双工管道)，尽管很多人在编程中使用过管道，但一些特殊的用法还是鲜有文章涉及，本文给出了多个具体的实例，每个实例均附有完整的源代码；本文所有实例在 Ubuntu 20.04 上编译测试通过，gcc版本号为：9.4.0；本文适合 Linux 编程的初学者阅读
" /><meta name="keywords" content="linux, socket, hugo, dos" />






<meta name="generator" content="Hugo 0.97.3 with theme even" />


<link rel="canonical" href="https://whowin.gitee.io/post/blog/ipc/0010-ipc-example-of-anonymous-pipe/" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">



<link href="/sass/main.min.e3fea119b1980e848b03dffbeddb11dd0fba483eed0e5f11870fb8e31f145bbd.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.css" integrity="sha256-7TyXnr2YU040zfSP+rEcz29ggW4j56/ujTPwjMzyqFY=" crossorigin="anonymous">


<meta property="og:title" content="IPC之一：使用匿名管道进行父子进程间通信的例子" />
<meta property="og:description" content="IPC 是 Linux 编程中一个重要的概念，IPC 有多种方式，本文主要介绍匿名管道(又称管道、半双工管道)，尽管很多人在编程中使用过管道，但一些特殊的用法还是鲜有文章涉及，本文给出了多个具体的实例，每个实例均附有完整的源代码；本文所有实例在 Ubuntu 20.04 上编译测试通过，gcc版本号为：9.4.0；本文适合 Linux 编程的初学者阅读" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://whowin.gitee.io/post/blog/ipc/0010-ipc-example-of-anonymous-pipe/" /><meta property="article:section" content="post" />
<meta property="article:published_time" content="2023-07-28T16:43:29+08:00" />
<meta property="article:modified_time" content="2023-07-28T16:43:29+08:00" />

<meta itemprop="name" content="IPC之一：使用匿名管道进行父子进程间通信的例子">
<meta itemprop="description" content="IPC 是 Linux 编程中一个重要的概念，IPC 有多种方式，本文主要介绍匿名管道(又称管道、半双工管道)，尽管很多人在编程中使用过管道，但一些特殊的用法还是鲜有文章涉及，本文给出了多个具体的实例，每个实例均附有完整的源代码；本文所有实例在 Ubuntu 20.04 上编译测试通过，gcc版本号为：9.4.0；本文适合 Linux 编程的初学者阅读"><meta itemprop="datePublished" content="2023-07-28T16:43:29+08:00" />
<meta itemprop="dateModified" content="2023-07-28T16:43:29+08:00" />
<meta itemprop="wordCount" content="5549">
<meta itemprop="keywords" content="Linux,IPC,进程间通信,pipe,匿名管道," /><meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="IPC之一：使用匿名管道进行父子进程间通信的例子"/>
<meta name="twitter:description" content="IPC 是 Linux 编程中一个重要的概念，IPC 有多种方式，本文主要介绍匿名管道(又称管道、半双工管道)，尽管很多人在编程中使用过管道，但一些特殊的用法还是鲜有文章涉及，本文给出了多个具体的实例，每个实例均附有完整的源代码；本文所有实例在 Ubuntu 20.04 上编译测试通过，gcc版本号为：9.4.0；本文适合 Linux 编程的初学者阅读"/>

<!--[if lte IE 9]>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
<![endif]-->

<!--[if lt IE 9]>
  <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
<![endif]-->

  <script async src="/js/busuanzi.pure.mini.js"></script><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9724909319263152"
     crossorigin="anonymous"></script>


</head>
<body>
  <div id="mobile-navbar" class="mobile-navbar">
  <div class="mobile-header-logo">
    <a href="/" class="logo">WhoWin</a>
  </div>
  <div class="mobile-navbar-icon">
    <span></span>
    <span></span>
    <span></span>
  </div>
</div>
<nav id="mobile-menu" class="mobile-menu slideout-menu">
  <ul class="mobile-menu-list">
    <a href="/">
        <li class="mobile-menu-item">首页</li>
      </a><a href="/post/">
        <li class="mobile-menu-item">文章归档</li>
      </a><a href="/article-categories/categories/">
        <li class="mobile-menu-item">文章分类</li>
      </a><a href="/tags/">
        <li class="mobile-menu-item">文章标签</li>
      </a><a href="/about/about/">
        <li class="mobile-menu-item">关于</li>
      </a>
  </ul>

  


</nav>

  <div class="container" id="mobile-panel">
    <header id="header" class="header">
        <div class="logo-wrapper">
  <a href="/" class="logo">WhoWin</a>
  
  <div style="position:absolute; left: 80px; top: 75px; color: crimson">
      ———开源和分享是技术发展的源泉和动力；本博客所有文章均为原创
  </div>
</div>





<nav class="site-navbar">
  <ul id="menu" class="menu">
    <li class="menu-item">
        <a class="menu-item-link" href="/">首页</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/post/">文章归档</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/article-categories/categories/">文章分类</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/tags/">文章标签</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/about/about/">关于</a>
      </li>
  </ul>
</nav>

    </header>

    <main id="main" class="main">
      <div class="content-wrapper">
        <div id="content" class="content">
          <article class="post">
    
    <header class="post-header">
      <h1 class="post-title">IPC之一：使用匿名管道进行父子进程间通信的例子</h1>

      <div class="post-meta">
        <span class="post-time"> 2023-07-28 </span>
        <div class="post-category">
            <a href="/categories/ipc/"> IPC </a>
            <a href="/categories/linux/"> Linux </a>
            <a href="/categories/c-language/"> C Language </a>
            </div>
        
      </div>
    </header>

    <div class="post-toc" id="post-toc">
  <h2 class="post-toc-title">文章目录</h2>
  <div class="post-toc-content always-active">
    <nav id="TableOfContents">
  <ul>
    <li>
      <ul>
        <li><a href="#1-概述">1 概述</a></li>
        <li><a href="#2-管道的基本概念">2. 管道的基本概念</a></li>
        <li><a href="#3-如何用c语言创建管道">3 如何用C语言创建管道</a></li>
        <li><a href="#4-在管道上使用-dup">4 在管道上使用 dup()</a></li>
        <li><a href="#5-使用管道的简单方法">5 使用管道的简单方法</a></li>
        <li><a href="#6-管道的原子操作">6 管道的原子操作</a></li>
        <li><a href="#7-匿名管道的其它说明">7 匿名管道的其它说明</a></li>
        <li><a href="#欢迎订阅-进程间通信专栏httpsblogcsdnnetwhowincategory_12404164html"><strong>欢迎订阅 <a href="https://blog.csdn.net/whowin/category_12404164.html">『进程间通信专栏』</a></strong></a></li>
      </ul>
    </li>
  </ul>
</nav>
  </div>
</div>
    <div class="post-content">
      <p>IPC 是 Linux 编程中一个重要的概念，IPC 有多种方式，本文主要介绍匿名管道(又称管道、半双工管道)，尽管很多人在编程中使用过管道，但一些特殊的用法还是鲜有文章涉及，本文给出了多个具体的实例，每个实例均附有完整的源代码；本文所有实例在 Ubuntu 20.04 上编译测试通过，gcc版本号为：9.4.0；本文适合 Linux 编程的初学者阅读</p>
<h2 id="1-概述">1 概述</h2>
<ul>
<li>IPC(Inter-Process Communication) - 进程间通信，提供了各种进程间通信的方法；</li>
<li>在 Linux C 编程中，IPC 通常有如下几种方式
<ol>
<li>半双工管道(Unix Pipe)，简称<strong>管道</strong>，又称为匿名管道</li>
<li>FIFOs - 又称为命名管道</li>
<li>消息队列(Message Queues)</li>
<li>信号量集(Semaphore Sets)</li>
<li>共享内存(Shared Memory Segments)</li>
<li>网络 socket(AF_INET Address Family)</li>
<li>全双工管道(AF_UNIX Address family)</li>
</ol>
</li>
<li>本文主要介绍匿名管道(Unix Pipe)的应用场景及使用方法，并给出多个附有完整源代码的实例；</li>
<li>管道又被称为 <strong>匿名管道</strong>，是相对于命名管道而言的；</li>
<li>匿名管道的通信模式是半双工的，所谓半双工指的是在管道中数据流是单方向的，当 A 进程和 B 进程之间使用管道进行通信时，数据要么从 A 发向 B，要么从 B 发向 A，在一个管道上，不能既有 A 向 B 的数据流，又有 B 向 A 的数据流；</li>
<li>管道还有一个特性就是只能在有亲缘关系的进程间传递消息，换句话说，只有当两个进程有相同的祖先时，才有可能使用管道进行通信。</li>
</ul>
<h2 id="2-管道的基本概念">2. 管道的基本概念</h2>
<ul>
<li>
<p>简单地说，管道是一种将一个进程的输出连接到另一个进程的输入的方法；</p>
</li>
<li>
<p>管道是最古老的 IPC 工具，从最早的 UNIX 操作系统开始就存在了；它们提供了进程间单向通信的方法(因此称为半双工)；</p>
</li>
<li>
<p>实际上，管道的这个特性广泛应用在 Linux 的命令行上，比如下面的命令：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ls <span class="p">|</span> sort <span class="p">|</span> lp
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>这条命令实际上就建立了一个管道，将 <code>ls</code> 的输出作为 <code>sort</code> 的输入，将 <code>sort</code> 的输出作为 <code>lp</code> 的输入；数据在匿名管道中运行，看上去，数据在管道中从左向右单方向流动；</p>
</li>
<li>
<p>管道是在 <code>Linux</code> 内核中实现的，很多程序员在 <code>shell</code> 脚本编程中都会频繁使用管道，但很少有人会去想管道在 <code>Linux</code> 内核中是如何实现的；</p>
<blockquote>
<p>当一个进程创建管道时，内核会创建两个文件描述符(<code>fd[0]</code> 和 <code>fd[1]</code>)供管道使用；一个描述符(<code>fd[1]</code>)用于将数据写入管道，另一个描述符(<code>fd[0]</code>)用于从管道中读取数据；此时，管道的实际用处不大，因为创建管道的进程只能使用管道与自身进行通信，毫无意义；</p>
</blockquote>
</li>
<li>
<p>下图展示了一个进程创建管道后，进程与内核的关系：</p>
<p><img src="https://whowin.gitee.io/images/190010/pipe-process-kernel.png" alt="process and kernel"></p>
</li>
<li>
<p>从上图中，可以看出以下几点：</p>
<ul>
<li>文件描述符是如何连接在一起的；进程通过文件描述符(<code>fd[1]</code>)向管道写入数据，也能够从文件描述符(<code>fd[0]</code>)从管道中读取该数据；</li>
<li>通过管道传输数据时，数据是通过内核流动的；在 <code>Linux</code> 下，管道在内核内部使用 <code>inode</code> 表示，<code>innode</code> 驻留在内核中，并不属于一个物理文件系统。</li>
</ul>
</li>
<li>
<p>这样建立的管道毫无用处，一个进程要自言自语，没有必要建立一个管道；但是，如果创建管道的进程再 <code>fork</code> 出一个子进程，由于子进程会从父进程继承管道的描述符，这样父子进程之间有可以通过这个管道进行通信了；</p>
</li>
<li>
<p>下图描述了父进程、子进程和内核的关系</p>
<p><img src="https://whowin.gitee.io/images/190010/pipe-parent-child-process-kernel.png" alt="parent-child processes and kernel"></p>
</li>
<li>
<p>从上图中，我们可以看到，父进程和子进程都可以访问管道的两个文件描述符，但是很显然，如果父进程和子进程同时向 <code>fd[1]</code> 写入数据，一定会造成混乱，而且如果父、子进程均向 <code>fd[1]</code> 写入数据，当从 <code>fd[0]</code> 读出数据时，并无法区分读到的数据是那个进程写入的；所以必须要做出抉择，这个建立的管道的数据是向那个方向流动，从父进程流向子进程？还是从子进程流向父进程？两个进程必须达成一致，否则会出现混乱；</p>
</li>
<li>
<p>为了讨论方便，我们假定子进程要做一些事务，然后把结果通过管道发送给父进程，如下面图示：</p>
<p><img src="https://whowin.gitee.io/images/190010/data-from-child-to-parent.png" alt="Data flows from child to parent process"></p>
</li>
<li>
<p>至此，管道已经建立完毕，下面就是如何使用管道；前面提到过，管道的文件描述符使用 <code>inode</code>，所以可以使用低级文件 I/O 的系统调用来直接访问管道；</p>
</li>
<li>
<p>向管道中写入数据，使用 <code>write()</code> 系统调用；从管道中读出数据，使用 <code>read()</code> 系统调用；</p>
</li>
<li>
<p><strong>特别提醒</strong>：系统调用 <code>lseek()</code> 不能在管道中使用。</p>
</li>
</ul>
<h2 id="3-如何用c语言创建管道">3 如何用C语言创建管道</h2>
<ul>
<li>使用 <code>pipe()</code> 系统调用可以创建一个管道，这个调用需要一个由两个整数组成的数组作为参数，调用成功后，该数组将包含管道的两个文件描述符；</li>
<li>系统调用：pipe()
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">原型：#include &lt;unistd.h&gt;
</span></span><span class="line"><span class="cl">     int pipe(int fd[2]);
</span></span><span class="line"><span class="cl">返回：调用成功返回 0
</span></span><span class="line"><span class="cl">     调用失败返回 -1
</span></span><span class="line"><span class="cl">     error = EMFILE (no free descriptors)
</span></span><span class="line"><span class="cl">             EMFILE (system file table is full)
</span></span><span class="line"><span class="cl">             EFAULT (fd array is not valid)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">备注: fd[0] 用于从管道中读取数据, fd[1] 用于向管道中写入数据
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>调用成功后，不仅两个管道描述符被建立，而且处于打开状态，可以直接进行读、写操作；</li>
<li>再次重申，所有通过管道传输的数据都要通过内核；下面是使用 <code>pipe()</code> 建立管道的代码：
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;sys/types.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span>     <span class="n">fd</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">pipe</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>前面说过，这样建立的管道毫无用处，进程自言自语并不需要使用管道；要使管道有意义，在建立管道后要 <code>fork()</code> 一个子进程；
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;sys/types.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span>   <span class="n">fd</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">pid_t</span> <span class="n">childpid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">pipe</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">((</span><span class="n">childpid</span> <span class="o">=</span> <span class="n">fork</span><span class="p">())</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">perror</span><span class="p">(</span><span class="s">&#34;fork&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>如果父进程要从子进程接收数据，父进程应关闭向管道写入的描述符 <code>fd[1]</code>，而子进程应该关闭从管道读出的描述符 <code>fd[0]</code>；如果父进程要向子进程发送数据，则父进程应关闭从管道读出的描述符 <code>fd[0]</code>，而子进程应该关闭向管道写入的描述符 <code>fd[1]</code>；</li>
<li>由于管道描述符在父进程和子进程之间是共享的，所以我们要确保关闭掉我们不需要的管道末端，从技术上讲，如果不需要的管道末端没有关闭，则永远不会返回 <code>EOF</code>；</li>
<li>下面代码假定父进程要从子进程接收数据：
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;sys/types.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span>   <span class="n">fd</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">pid_t</span> <span class="n">childpid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">pipe</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">((</span><span class="n">childpid</span> <span class="o">=</span> <span class="n">fork</span><span class="p">())</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">perror</span><span class="p">(</span><span class="s">&#34;fork&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">childpid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Child process closes up input side of pipe */</span>
</span></span><span class="line"><span class="cl">        <span class="n">close</span><span class="p">(</span><span class="n">fd</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Parent process closes up output side of pipe */</span>
</span></span><span class="line"><span class="cl">        <span class="n">close</span><span class="p">(</span><span class="n">fd</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>如前所述，建立了管道以后，就可以像对待普通文件描述符一样对待管道描述符；</li>
<li>源程序：<a href="https://gitee.com/whowin/whowin/blob/blog/sourcecodes/190010/pipe.c">pipe.c</a>(<strong>点击文件名下载源程序</strong>)演示了子进程向父进程发送信息：Hello, world!</li>
</ul>
<h2 id="4-在管道上使用-dup">4 在管道上使用 dup()</h2>
<ul>
<li>
<p>大多数已有的 <code>Linux</code> 命令或者自己编写的程序，其默认的输入设备往往是 <code>STDIN</code>，而输出设备是 <code>STDOUT</code>，当我们希望在程序中用某个 <code>Linux</code> 命令处理数据时，往往不太好获得命令的输出，或者不好把数据传送给这个程序，这时候管道可以发挥作用；</p>
</li>
<li>
<p>比如 <code>Linux</code> 命令 <code>sort</code>，在没有其它参数时，其默认的输入设备就是 <code>STDIN</code>，当我们在程序中希望使用 <code>sort</code> 处理一组数据时，我们可以设法把 <code>STDIN</code> 连接到管道的输出端，这样，我们向管道中的一端写入数据时，管道的另一端已经启动的 <code>sort</code> 就可以从 <code>STDIN</code> 读到数据并进行处理；</p>
</li>
<li>
<p>系统调用 <code>dup()</code> 和 <code>dup2()</code> 可以帮助我们实现这个想法；先看一下这两个系统调用的说明；</p>
</li>
<li>
<p>系统调用: dup();</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">原型：#include &lt;unistd.h&gt;
</span></span><span class="line"><span class="cl">     int dup(int oldfd);
</span></span><span class="line"><span class="cl">说明：dup() 系统调用创建文件描述符 oldfd 的副本，使用编号最小的未使用的文件描述符作为新描述符。
</span></span><span class="line"><span class="cl">返回：调用成功则返回新描述符
</span></span><span class="line"><span class="cl">     调用失败则返回 -1
</span></span><span class="line"><span class="cl">     errno = EBADF (oldfd is not a valid descriptor)       
</span></span><span class="line"><span class="cl">             EBADF (newfd is out of range)
</span></span><span class="line"><span class="cl">             EMFILE (too many descriptors for the process) 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">备注：oldfd 不会被关闭，新描述符和 oldfd 都可以使用。
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>系统调用：dup2();</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">原型：#include &lt;unistd.h&gt;
</span></span><span class="line"><span class="cl">     int dup2(int oldfd, int newfd);
</span></span><span class="line"><span class="cl">说明：dup2() 系统调用与 dup() 相似，创建文件描述符 oldfd 的副本，但它不使用编号最小的未使用文件描述符，而是使用 newfd 中指定的文件描述符；
</span></span><span class="line"><span class="cl">     如果文件描述符 newfd 先前已打开，该调用会首先将其关闭然后再使用。
</span></span><span class="line"><span class="cl">返回：调用成功则返回新描述符
</span></span><span class="line"><span class="cl">     调用失败则返回 -1
</span></span><span class="line"><span class="cl">     errno = EBADF (oldfd is not a valid descriptor)
</span></span><span class="line"><span class="cl">             EBADF (newfd is out of range)
</span></span><span class="line"><span class="cl">             EMFILE (too many descriptors for the process)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">备注：使用 dup2()，oldfd 会被关闭
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>在子进程中，使用 <code>dup2()</code> 将管道的输出(<code>fd[0]</code>)复制到 <code>STDIN</code> 上，并关闭 <code>STDIN</code>，然后用 <code>exec()</code> 启动 <code>sort</code> 时，当 <code>sort</code> 从 <code>STDIN</code> 读入数据时，实际上是从管道中读出数据，当我们从父进程向管道中写入数据时，这个数据将被 <code>sort</code> 读取并处理；</p>
</li>
<li>
<p>为了搞清楚这种用法，请自行学习 Linux 命令 <strong>sort</strong>，可以用在线手册 <code>man sort</code> 了解该命令的详细信息；</p>
</li>
<li>
<p>下图或许可以更直观地描述这种使用方法：</p>
<p><img src="https://whowin.gitee.io/images/190010/pipe-with-dup.png" alt="pipe with dup()"></p>
</li>
<li>
<p>源程序：<a href="https://gitee.com/whowin/whowin/blob/blog/sourcecodes/190010/pipe-dup-stdin.c">pipe-dup-stdin.c</a>(<strong>点击文件名下载源程序</strong>)演示了在管道中使用 <code>dup2()</code> 将 fd[0] 复制到 <code>STDIN</code> 的方法：</p>
<blockquote>
<p>子进程中把 fd[0] 复制到 <code>STDIN</code>，然后启动 <code>sort</code>，父进程向管道中写入若干个单词，每个单词以 <code>\n</code> 结尾，<code>sort</code> 从 <code>STDIN</code> 读入数据，实际上是从管道中读入数据，所以 <code>sort</code> 程序会对这些单词进行排序，并把结果写入文件 <code>sort.log</code> 中，程序运行完毕后，使用 <code>cat sort.log</code> 可以看到经过排序的单词；</p>
</blockquote>
</li>
<li>
<p>同样道理，也可以在子进程中把管道的输入端(<code>fd[1]</code>)复制到 <code>STDOUT</code> 上，这样，当子进程中启动的程序向 <code>STDOUT</code> 输出时，实际上是在向管道上写入数据；</p>
</li>
<li>
<p>源程序：<a href="https://gitee.com/whowin/whowin/blob/blog/sourcecodes/190010/pipe-dup-stdout.c">pipe-dup-stdout.c</a>(<strong>点击文件名下载源程序</strong>)演示了在管道中使用 <code>dup2()</code> 将 fd[1] 复制到 <code>STDOUT</code> 的方法：</p>
<blockquote>
<p>子进程中把 <code>fd[1]</code> 复制到 <code>STDOUT</code>，然后启动 <code>uname</code>，<code>uname -r</code> 会输出一个字符串到 <code>STDOUT</code>，实际上是写入到了管道中，父进程从管道中收到了这个字符串并显示出来；</p>
</blockquote>
<blockquote>
<p>子进程中把管道的输入端复制到 <code>STDOUT</code> 后，在子进程中启动任何程序，在主进程中通过读取管道都可以轻易地获得这个程序的输出，比如我们要知道当前系统的是不是 64 位系统，那我们在子进程中启动命令 <code>uname -m</code>，如果主进程在管道上读出的内容是 <code>x86_64</code>，则系统无疑是64位的。</p>
</blockquote>
</li>
</ul>
<h2 id="5-使用管道的简单方法">5 使用管道的简单方法</h2>
<ul>
<li>
<p>上面介绍的在程序中使用管道获取一个外部程序的输出(或者向一个外部程序输入数据)的方法看上去不仅繁琐，而且绕的弯也比较多，其实使用管道还有更为简单的方法；</p>
</li>
<li>
<p>使用标准库函数 <code>popen()</code> 可以很容易地使用管道；</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">库函数：popen();
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">原型：#include &lt;stdio.h&gt;
</span></span><span class="line"><span class="cl">     FILE *popen (char *command, char *type);
</span></span><span class="line"><span class="cl">说明：popen() 函数通过创建一个管道，调用 fork 产生一个子进程，执行 shell 运行命令来开启一个进程。
</span></span><span class="line"><span class="cl">返回：调用成功则返回一个标准 I/O 流
</span></span><span class="line"><span class="cl">     调用 fork() 或 pipe() 失败则返回 NULL
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>该标准库函数通过内部调用 <code>pipe()</code> 创建匿名管道，然后 <code>fork()</code> 一个子进程，执行 <code>shell</code>，并在 <code>shell</code> 中执行 &ldquo;command&rdquo; 参数；数据流的方向由第二个参数 <code>type</code> 确定，<code>type</code> 可以是 &ldquo;r&rdquo; 或 &ldquo;w&rdquo;，表示读或写，不可能两者兼而有之！在 <code>Linux</code> 下，管道将以 <code>type</code> 参数的第一个字符指定的模式打开，如果您将 <code>type</code> 设置为 &ldquo;rw&rdquo;，该函数会以 &ldquo;r&rdquo; (读)模式打开管道。</p>
</li>
<li>
<p>与直接使用 <code>pipe()</code> 系统调用相比，这个库函数为我们做了很多繁琐的工作，但却让我们失去了对整个过程的精细控制；</p>
</li>
<li>
<p>该函数直接使用了 Bourne shell(bash), 所以在 <code>command</code> 参数中可以使用 <code>shell</code> 元字符以及元字符扩展(包括通配符)；</p>
</li>
<li>
<p>使用 <code>popen()</code> 创建的管道必须使用 <code>pclose()</code> 关闭；<code>popen()/pclose()</code> 与标准文件流I/O函数 <code>fopen()/fclose()</code> 非常相似。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">库函数：pclose();
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">原型：#include &lt;stdio.h&gt;
</span></span><span class="line"><span class="cl">     int pclose(FILE *stream);
</span></span><span class="line"><span class="cl">说明：pclose()函数等待相关进程终止，并返回由 wait4() 返回的命令退出状态。
</span></span><span class="line"><span class="cl">返回：返回 wait4() 调用的退出状态码
</span></span><span class="line"><span class="cl">     如果 stream 不合法，或者 wait() 执行失败，则返回 -1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">备注：等待管道进程退出，然后关闭文件 I/O 流
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><code>pclose()</code> 函数对由 <code>popen()</code> 派生的进程执行 <code>wait4()</code>，当 <code>wait4()</code> 返回时，它会销毁管道和文件流；</p>
</li>
<li>
<p>源程序：<a href="https://gitee.com/whowin/whowin/blob/blog/sourcecodes/190010/pipe-popen.c">pipe-popen.c</a>(<strong>点击文件名下载源程序</strong>)完成与前面的例子 <a href="https://gitee.com/whowin/whowin/blob/blog/sourcecodes/190010/pipe-dup-stdin.c">pipe-dup-stdin.c</a> 一样的功能，但看上去要简单的多；</p>
</li>
<li>
<p>由于 <code>popen()</code> 使用 <code>shell</code> 来执行命令，因此 <code>shell</code> 扩展字符和元字符都可以使用，此外，使用 popen() 打开管道时，可以使用一些高级技术来执行命令，例如重定向，甚至输出管道；以下调用示例分别使用了扩展字符、重定向和输出管道：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="n">popen</span><span class="p">(</span><span class="s">&#34;ls ~scottb&#34;</span><span class="p">,</span> <span class="s">&#34;r&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">popen</span><span class="p">(</span><span class="s">&#34;sort &gt; /tmp/foo&#34;</span><span class="p">,</span> <span class="s">&#34;w&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">popen</span><span class="p">(</span><span class="s">&#34;sort | uniq | more&#34;</span><span class="p">,</span> <span class="s">&#34;w&#34;</span><span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>源程序：<a href="https://gitee.com/whowin/whowin/blob/blog/sourcecodes/190010/pipe-popen2.c">pipe-popen2.c</a>(<strong>点击文件名下载源程序</strong>)打开了两个管道，一个用于 <code>ls</code> 命令，另一个用于 <code>sort</code> 命令；</p>
</li>
<li>
<p>下面这个例子试图编写一个通用的管道程序，源程序文件：<a href="https://gitee.com/whowin/whowin/blob/blog/sourcecodes/190010/pipe-popen3.c">pipe-popen3.c</a>(<strong>点击文件名下载源程序</strong>)</p>
<ul>
<li>使用方法为：<code>./pipe-popen3 [command] [filename]</code></li>
<li>该程序会首先打开文件 <code>filename</code></li>
<li>然后使用 <code>popen()</code> 以写方式打开 <code>command</code> 管道</li>
<li>从 <code>filename</code> 中读出内容并写入管道</li>
<li>可以尝试用以下方式测试这个例子
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./pipe-popen3 sort pipe-popen3.c
</span></span><span class="line"><span class="cl">./pipe-popen3 cat pipe-popen3.c
</span></span><span class="line"><span class="cl">./pipe-popen3 more pipe-popen3.c
</span></span><span class="line"><span class="cl">./pipe-popen3 cat pipe-popen3.c <span class="p">|</span> grep main
</span></span></code></pre></td></tr></table>
</div>
</div></li>
</ul>
</li>
</ul>
<h2 id="6-管道的原子操作">6 管道的原子操作</h2>
<ul>
<li>所谓“原子操作”，是指一个或一系列不可中断的操作，就是说一个原子操作一旦开始执行就不能被中断，直至执行完毕；</li>
<li>POSIX 标准规定了管道上原子操作的最大缓冲区大小是 512 字节，定义在头文件：<code>bits/posix1_lim.h</code> 中：
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#define _POSIX_PIPE_BUF         512
</span></span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>根据这一定义，如果一次写入/读出管道的操作大于 512 字节，操作将是非“原子操作”，也就是写入/读出的数据可能会被分割；</li>
<li>在 Linux 下，定义的管道上的原子操作的最大缓冲区大小为：4096 字节，定义在头文件：<code>linux/limits.h</code> 中：
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#define PIPE_BUF        4096	</span><span class="cm">/* # bytes in atomic write to a pipe */</span><span class="cp">
</span></span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>显然，在我们目前的环境下，在管道上进行不大于 4096 字节的读/写操作是原子操作；</li>
<li>在多进程环境下，原子操作对管道的读/写操作非常重要，当一个进程写入管道的数据大于阈值时，其写入过程中间会中断，操作系统会产生进程调度，如果这时其它进程也向这个管道写入数据，那么写入管道的数据会产生混乱。</li>
</ul>
<h2 id="7-匿名管道的其它说明">7 匿名管道的其它说明</h2>
<ul>
<li>尽管管道是半双工的，但是，打开两个管道，并在子进程中合理地重新分配描述符，可以构建出一个类似全双工的管道；</li>
<li><code>pipe()</code> 调用必须在 <code>fork()</code> 调用之前进行，否则描述符将不会被子进程继承；</li>
<li>使用匿名管道进行通信的进程都必须有一个共同的祖先，而且这个祖先必须是管道的创建者，由于管道位于内核中，不在管道创建者祖先中的进程都无法对其进行寻址，这与命名管道(FIFO)是不同；</li>
<li>由于使用管道的一些限制，在进程间进行通讯时，管道实际上并不是一个常用的方法，但是，如果需要使用已有的 Linux 命令处理数据，或者从 Linux 命令获得结果数据，管道不失为一个好的选择；</li>
<li>匿名管道的生命周期与创建它的进程的生命周期一致，当进程结束时，其创建的匿名管道也将被销毁；</li>
<li>尽管在本文的所有实例中，从管道传输的数据都是字符串，但管道是可以传输二进制数据的，也就是说，可以把一个结构完整第通过管道进行传输。</li>
<li>有关进程间通信(IPC)的的其它文章：
<ul>
<li><a href="https://whowin.gitee.io/post/blog/ipc/0010-ipc-example-of-anonymous-pipe/">IPC之一：使用匿名管道进行父子进程间通信的例子</a></li>
<li><a href="https://whowin.gitee.io/post/blog/ipc/0011-ipc-examples-of-fifo/">IPC之二：使用命名管道(FIFO)进行进程间通信的例子</a></li>
<li><a href="https://whowin.gitee.io/post/blog/ipc/0013-systemv-message-queue/">IPC之三：使用 System V 消息队列进行进程间通信的实例</a></li>
<li><a href="https://whowin.gitee.io/post/blog/ipc/0014-posix-message-queue/">IPC之四：使用 POSIX 消息队列进行进程间通信的实例</a></li>
<li><a href="https://whowin.gitee.io/post/blog/ipc/0015-systemv-semaphore-sets/">IPC之五：使用 System V 信号量集解决经典的‘哲学家就餐问题‘</a></li>
</ul>
</li>
</ul>
<h2 id="欢迎订阅-进程间通信专栏httpsblogcsdnnetwhowincategory_12404164html"><strong>欢迎订阅 <a href="https://blog.csdn.net/whowin/category_12404164.html">『进程间通信专栏』</a></strong></h2>
<hr>
<p><strong>欢迎访问我的博客：https://whowin.cn</strong></p>
<p><strong>email: <a href="mailto:hengch@163.com">hengch@163.com</a></strong></p>
<p><img src="https://whowin.gitee.io/images/qrcode/sponsor-qrcode.png" alt="donation"></p>
<!-- for CSDN
[article01]: https://blog.csdn.net/whowin/article/details/132171311
[article02]: https://blog.csdn.net/whowin/article/details/132171930
[article03]: https://blog.csdn.net/whowin/article/details/132172172
[article04]: https://blog.csdn.net/whowin/article/details/134869490
[article05]: https://blog.csdn.net/whowin/article/details/134869636
-->
<!-- CSDN
[img01]: https://img-blog.csdnimg.cn/img_convert/fca2c50ac5250408b48adcafd118595c.png
[img02]: https://img-blog.csdnimg.cn/img_convert/99baefca021c879ac587b625e815d461.png
[img03]: https://img-blog.csdnimg.cn/img_convert/8cdda105b707b332a6b05c9a48524e51.png
[img04]: https://img-blog.csdnimg.cn/img_convert/45dd62313ff5435233326ddc0eb12739.png
-->
    </div>

    <div class="post-copyright">
  <p class="copyright-item">
    <span class="item-title">文章作者</span>
    <span class="item-content">whowin</span>
  </p>
  <p class="copyright-item">
    <span class="item-title">上次更新</span>
    <span class="item-content">
        2023-07-28
        
    </span>
  </p>
  
  
</div>
<footer class="post-footer">
      <div class="post-tags">
          <a href="/tags/linux/">Linux</a>
          <a href="/tags/ipc/">IPC</a>
          <a href="/tags/%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1/">进程间通信</a>
          <a href="/tags/pipe/">pipe</a>
          <a href="/tags/%E5%8C%BF%E5%90%8D%E7%AE%A1%E9%81%93/">匿名管道</a>
          </div>
      <nav class="post-nav">
        <a class="prev" href="/post/blog/ipc/0011-ipc-examples-of-fifo/">
            <i class="iconfont icon-left"></i>
            <span class="prev-text nav-default">IPC之二：使用命名管道(FIFO)进行进程间通信的例子</span>
            <span class="prev-text nav-mobile">上一篇</span>
          </a>
        <a class="next" href="/post/blog/network/0022-how-to-scan-wifi-signal/">
            <span class="next-text nav-default">使用ioctl扫描wifi信号获取信号属性的实例(一)</span>
            <span class="next-text nav-mobile">下一篇</span>
            <i class="iconfont icon-right"></i>
          </a>
      </nav>
    </footer>
  </article>
        </div>
        

  <span id="/post/blog/ipc/0010-ipc-example-of-anonymous-pipe/" class="leancloud_visitors" data-flag-title="IPC之一：使用匿名管道进行父子进程间通信的例子">
		<span class="post-meta-item-text">文章阅读量 </span>
		<span class="leancloud-visitors-count">0</span>
		<p></p>
	  </span>
  <div id="vcomments"></div>
  <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
  <script src='//unpkg.com/valine/dist/Valine.min.js'></script>
  <script type="text/javascript">
    new Valine({
        el: '#vcomments' ,
        appId: 'OFCGzCfJRUglzOdzrqMGkbTR-gzGzoHsz',
        appKey: 'v7P29kPAEbsmaavaYPNhGhnF',
        notify:  false ,
        verify:  false ,
        avatar:'mm',
        placeholder: '说点什么吧...',
        visitor:  true 
    });
  </script>

  

      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="social-links">
      <a href="mailto:hengch@163.com" class="iconfont icon-email" title="email"></a>
  <a href="https://whowin.gitee.io/index.xml" type="application/rss+xml" class="iconfont icon-rss" title="rss"></a>
</div>
<div class="copyright">
  <span class="power-by">
    由 <a class="hexo-link" href="https://gohugo.io">Hugo</a> 强力驱动
  </span>
  <span class="division">|</span>
  <span class="theme-info">
    主题 - 
    <a class="theme-link" href="https://github.com/olOwOlo/hugo-theme-even">Even</a>
  </span>

  <div class="busuanzi-footer">
    <span id="busuanzi_container_site_pv"> 本站总访问量 <span id="busuanzi_value_site_pv"><img src="/img/spinner.svg" alt="spinner.svg"/></span> 次 </span>
      <span class="division">|</span>
    <span id="busuanzi_container_site_uv"> 本站总访客数 <span id="busuanzi_value_site_uv"><img src="/img/spinner.svg" alt="spinner.svg"/></span> 人 </span>
  </div>

  <span class="copyright-year">
    &copy; 
    2022 - 
    2024<span class="heart"><i class="iconfont icon-heart"></i></span><span>whowin</span>
  </span>
</div>

    </footer>

    <div class="back-to-top" id="back-to-top">
      <i class="iconfont icon-up"></i>
    </div>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/slideout@1.0.1/dist/slideout.min.js" integrity="sha256-t+zJ/g8/KXIJMjSVQdnibt4dlaDxc9zXr/9oNPeWqdg=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.js" integrity="sha256-XVLffZaxoWfGUEbdzuLi7pwaUJv1cecsQJQqGLe7axY=" crossorigin="anonymous"></script>



<script type="text/javascript" src="/js/main.min.64437849d125a2d603b3e71d6de5225d641a32d17168a58106e0b61852079683.js"></script>
  <script type="text/javascript">
    window.MathJax = {
      tex: {
        inlineMath: [['$','$'], ['\\(','\\)']],
        }
    };
  </script>
  <script async src="https://cdn.jsdelivr.net/npm/mathjax@3.0.5/es5/tex-mml-chtml.js" integrity="sha256-HGLuEfFcsUJGhvB8cQ8nr0gai9EucOOaIxFw7qxmd+w=" crossorigin="anonymous"></script>








</body>
</html>
